with Ada.Strings; use Ada.Strings;
with Ada.Strings.Fixed;
with BE_Interfaces; use BE_Interfaces;
with MIDI.Control_Events, MIDI.Meta_Events, MIDI.Events;
with MIDI.Traces; use MIDI.Traces;
with MIDI.Instruments;
procedure MIDI.XMIDI.Convert_To_Text (
   Filename : String;
   Result   : in out Text_Builder.Builder_Type
   )
is
   XMI : XMI_File_Type;
   TB  : Text_Builder.Builder_Type renames Result;

   function On_EVNT return Boolean
   is
      tempo           : Uint32_be := 60_000_000 / 120;
      clocks_per_beat : Uint8_t := 24;
      End_Of_Track    : Boolean := False;
      Banks           : array(Channel_Range) of Bank_Type := (others => (0, 0));

      procedure On_Midi_Event (m : Events.Midi_Event_Type)
      is
         procedure On_Control_Event
         is
            use Control_Events;
            ch : Channel_Range := m.Token mod 16;
         begin
            case M.Token is
               when Events.Note_On  =>
                  declare
                     dur : Uint32_vl := Uint32_vl'Input(XMI.As_Stream);
                     -- ms : Uint32_be := To_Microseconds(dur, tempo, clocks_per_beat)/1000;
                  begin
                     tb.Append(", dur =>" & dur'Img); -- ms'Img);
                  end;
               when Events.Program_Change =>
                  tb.Append("[" & Instruments.Get_Instrument_Name(m.As_Program_Change.Patch, To_Uint16(Banks(ch))) & "]");
               when Events.Controller_Change =>
                  -- 0 => bank msb 
                  -- 32 => bank lsb
                  case m.As_Controller_Change.Controller is
                     when 0  => Banks(ch).MSB := m.As_Controller_Change.Value;
                     when 32 => Banks(ch).LSB := m.As_Controller_Change.Value;
                     when others => null;
                  end case;
               when others =>
                  null;
            end case;
         end On_Control_Event;

         procedure On_Meta_Event
         is
            use Meta_Events;
            MB : Meta_Event(M.As_Meta_Head.Command, Natural(M.As_Meta_Head.Size));
         begin
            Meta_Event'Read(XMI.As_Stream, MB);
            tb.Append(Image(MB));
            case MB.Command is
               when Commands.End_Of_Track =>
                  End_Of_Track := True;
               when others =>
                  null;
            end case;
         end On_Meta_Event;

         procedure On_SysEx_Event
         is
         begin
            XMI.Next(Uint32_be(M.As_SysEx_Size));
         end On_SysEx_Event;
      begin
         tb.Append("<midi event => " & Image(M));
         case M.Token is
            when Events.Control => On_Control_Event;
            when Events.SysEx_Normal | Events.SysEx_Divided => On_SysEx_Event;
            when Events.Meta => On_Meta_Event;
            when others =>
               null;
         end case;
         tb.Append_Line(">");
      end On_Midi_Event;

      procedure On_Interval (B : Uint8_t)
      is
      begin
         tb.Append_Line("<interval => " & b'Img & ">");
      end On_Interval;
   begin
      tb.Inc_Indent;
      tb.New_Line;
      while not XMI.End_Of_File loop
         exit when End_Of_Track;
         declare
            m : Events.Midi_Event_Type := Events.Midi_Event_Type'Input(XMI.As_Stream);
         begin
            if m.Token < 128 then
               On_Interval(m.Token);
            else
               On_Midi_Event(m);
            end if;
         end;
      end loop;
      tb.Dec_Indent;
      return not End_Of_Track;
   end On_EVNT;

   function On_INFO return Boolean
   is
   begin
      tb.Append_Line("<forms count =>" & Unsigned_16'Input(XMI.As_Stream)'Img & ">");
      return True;
   end On_INFO;

   procedure On_Trunk_Begin (T : Trunk_Head_Type)
   is
   begin
      tb.Append(Image(T));
      tb.Inc_Indent;
      tb.New_Line;
   end On_Trunk_Begin;

   procedure On_Trunk_End (T : Trunk_Head_Type)
   is
   begin
      tb.Dec_Indent;
      tb.New_Line;
      tb.Append("END " & T.Name);
      tb.New_Line;
   end On_Trunk_End;

   procedure On_Path_Begin (Name : Trunk_Name_Type)
   is
   begin
      tb.Append_Line(Name);
   end On_Path_Begin;

   procedure On_Path_End (Name : Trunk_Name_Type)
   is
   begin
      null;
   end On_Path_End;

begin
   XMI.Open(Filename);
   XMI.Visit(
      On_INFO        => On_INFO'Access, 
      On_EVNT        => On_EVNT'Access,
      On_Trunk_Begin => On_Trunk_Begin'Access,
      On_Trunk_End   => On_Trunk_End'Access,
      On_Path_Begin  => On_Path_Begin'Access,
      On_Path_End    => On_Path_End'Access);
   XMI.Close;
exception
   when Stream_IO.End_Error => 
      XMI.Close;
end MIDI.XMIDI.Convert_To_Text;
